#!/usr/perl5/bin/perl -w
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
#

# auditxml_jni [-d] <xml input file>

# auditxml takes the audit record description (.xml file) and
# generates the files needed for the Java

use auditxml;
use Getopt::Std;
use vars qw($opt_d);
use strict;


our $debug = 0; # normal use is to set via the file being parsed.
               # <debug set="on"/> or <debug set="off"/> or <debug/>
               # if the set attribute is omitted, debug state is toggled
               # Override with appDebug, but toggle won't do what you
               # want.
my $appDebug = 0; # used after return from "new auditxml";

my $genNotice = "
DO NOT EDIT. This file is auto generated by the Solaris Audit
system from adt.xml.

See http://opensolaris.org/os/project/audit/
";

# trim leading/trailing newlines
$genNotice =~ s/^\n//s;
$genNotice =~ s/\n$//s;
my $prog = $0; $prog =~ s|.*/||g;
my $usage = "usage: $prog [-d] file.xml\n";

getopts('d');

$appDebug = $opt_d;

my $uniLabel = "adr";
my $xlateUniLabelInc = 0;

die $usage if ($#ARGV < 0);

# where everything comes from and where it goes:

my $templatePath = './';
my $javaPath     = $templatePath;
my $bsmBuildPath = "../libbsm";

my $jniBuildPath = "$javaPath";

my $buildPathJ	 = "$jniBuildPath/com/sun/audit";
my $buildPathJNI = "$jniBuildPath/common";

my $auditEventJ = "$buildPathJ/AuditEvent.java";
my $jniC = "$buildPathJNI/adt_jni_event.c";
my $mapFile = "$jniBuildPath/common/mapfile-vers";

my $doc = new auditxml ($ARGV[0]);  # input XML file

$debug = $appDebug;

my %jniEventTable = ();
my %externalIdNo = ();
my %msg_list = ();
my %eventCode = ();

readAuditEventFile("$bsmBuildPath/audit_event.txt");

my $event;
while ($event = $doc->getNextEvent()) {
    my $eventId = $event->getId();
    my $idNo = $event->getIdNo();
    $externalIdNo{$eventId} = $idNo;
    my $super;
    my $omit = $event->getOmit();
    my $eventType = '';
    if ($super = $event->getSuperClass()) {
	$event = $super;
	$eventType = 'instance';
    } else {
	$eventType = $event->getType();
    }

    # c file table for translation
    generateTableC($event, $eventId, $eventType, undef, $omit);
}

while (my $textList = $doc->getNextMsgId()) {
    generateMsgLists($textList);  # enum -> text mappings
}

printJavaFiles($jniC, $auditEventJ, $buildPathJ, $mapFile);

exit 0;



sub printJavaFiles {
    my $jniFile = shift;
    my $javaFile = shift;
    my $subclassPath = shift;
    my $mapFile = shift;

    # warning: time_t is equated to jlong since there is no
    # way to use sys/types.h in Java code.
    # java long is C long long, 64 bits.
    # java int is 32 bits.

    my %java_jni = ('ADT_DATE'		=> ['long',	'jlong'],
		    'ADT_UINT'		=> ['int',	'jint'],
		    'ADT_INT'		=> ['int',	'jint'],
		    'ADT_INT32'		=> ['int',	'jint'],
		    'ADT_UID'		=> ['int',	'jint'],
		    'ADT_GID'		=> ['int',	'jint'],
		    'ADT_UIDSTAR'	=> ['int[]',	'jintArray'],
		    'ADT_GIDSTAR'	=> ['int[]',	'jintArray'],
		    'ADT_CHAR'		=> ['String',	'jchar'],
		    'ADT_CHARSTAR'	=> ['String',	'jstring'],
		    'ADT_CHAR2STAR'	=> ['String[]',	'jstring'],
		    'ADT_MSG'		=> ['int',	'jint'],
		    'ADT_PID'		=> ['int',	'jint'],
# ADT_PRIVSTAR omitted -- not implemented and the audit records that
# use it must be coded to emit no java.  We'll cross that bridge
# when someone in Java land needs to generate a priv token.
		    'ADT_LONG'		=> ['int',	'jint'],
		    'ADT_TERMIDSTAR'	=> ['String',	'jstring'],	# hostname -> termid
		    'ADT_ULONG'		=> ['int',	'jint'],
		    'ADT_UINT16'	=> ['int',	'jint'],
		    'ADT_UINT32'	=> ['int',	'jint'],
		    'ADT_UINT32STAR'	=> ['int[]',	'jintArray'],
# ADT_UINT32ARRAY omitted; no Java implementation yet
		    'ADT_UINT64'	=> ['long',	'jlong'],
		    'ADT_UINT64STAR'	=> ['long[]',	'jlongArray']
		   );
    my $noMemory = 'gettext("Out of memory")';

    # open output files
    open (Cfile, ">$jniFile") or
	die "can't open output file ($jniFile): $!\n";
    open (Jfile, ">$javaFile") or
	die "can't open output file ($javaFile): $!\n";
    open (MapFile, ">$mapFile") or
	die "can't open output file ($mapFile): $!\n";

    # write headers
    my $notice = $genNotice;
    $notice =~ s/\n/\n * /gs;
    $notice =~ s/\s+\n/\n/gs;
    print Cfile <<EOF;
/*
 * $notice
 */

#include "../../libbsm/common/adt_xlate.h"
#include <jni.h>
#include "../com/sun/audit/AuditSession.h"	/* javah output */
#include "adt_jni.h"
#include <stdlib.h>
#include <string.h>

static char *except_class = "java/lang/Exception";

EOF
    print Jfile <<EOF;
/*
 * $notice
 */

package com.sun.audit;

public class AuditEvent {
	protected AuditSession sh;	// associated session object

	public AuditEvent(AuditSession auSession)
	    throws Error
	{

		sh = auSession;
	}

	// Manifest values: keep them in sync with generated <bsm/adt_event.h>.
	// It is generated by \$SRC/lib/libbsm/auditxml

	public static final int ADT_SUCCESS = 0;	// generated
	public static final int ADT_FAILURE = -1;	// generated

	// See the subclasses of AuditEvent for mapping message codes
	// to events
EOF

    my $notice_map = $genNotice;
    $notice_map =~ s/\n/\n# /gs;
    $notice_map =~ s/\s+\n/\n/gs;
    print MapFile <<EOF;
#
# $notice_map
#

\$mapfile_version 2

SYMBOL_VERSION SUNWprivate_1.1 {
    global:
	c2j_pointer;
	j2c_pointer;
	Java_com_sun_audit_AuditSession_bsmAuditOn;
	Java_com_sun_audit_AuditSession_startSession;
	Java_com_sun_audit_AuditSession_endSession;
	Java_com_sun_audit_AuditSession_dupSession;
	Java_com_sun_audit_AuditSession_getSessionId;
	Java_com_sun_audit_AuditSession_exportSessionData;
	Java_com_sun_audit_AuditSession_sessionAttr;

# One subclass of AuditEvent per audit record...
EOF

    # generate java final int classes to line up with string/enums

    foreach my $listName (sort keys %msg_list) {
        my $shortName = uc $listName;
        $shortName =~ s/_TEXT//;
        my ($listRef, $headref) = @{$msg_list{$listName}};
        my @listValue =  @$listRef;
        my ($header, $enumValue, $public, $deprecated) = @$headref;
        my $listValue;
    
        print Jfile "\n\t// adt_$listName" . "\n\n";
        print Jfile "\tpublic static final int ADT_$shortName",
    		" = $enumValue;\n" if $enumValue;
    
        next unless ($#listValue >= 0);
        print Jfile "\t// Deprecated message list\n" if $deprecated;
        foreach $listValue (@listValue) {
            my ($id, $text) = split(/\s*::\s*/, $listValue);
    	    print Jfile "\t// $text\n";
    	    print Jfile "\tpublic static final int ADT_$shortName";
	    print Jfile "_$id = $enumValue;\n";
    	    $enumValue++;
        }
    }

    # generate event creation and access functions and event
    # generation for both Java and JNI
    # com.sun.audit.AuditEvent_xxx.java
    foreach my $eventId (sort keys %jniEventTable) {
	my ($ref1, $eventType, $allowedIds, $header) = @{$jniEventTable{$eventId}};
	$eventCode{$eventId} = -1 if ($eventType eq 'generic');
	my @entries = @$ref1;
	my $entries = $#entries;
	my $root = $eventId;
	$root =~ s/AUE_//;
	my $javaPutEvent = 'putEvent';
	my $putMethod = "_$root";
	$putMethod =~ s/_/_1/g;
	    
	my $jniPutEvent = "Java_com_sun_audit_AuditEvent$putMethod" . "_$javaPutEvent";

	# the subclass file template isn't used; it may be needed to get
	# the right file header stuff in place.  The subclassPath is
	# the directory that contains 'em.

	my $validSfile = 1;
	unless (open(Sfile, ">$subclassPath/AuditEvent_$root.java")) {
	    print STDERR "can't open class file AuditEvent_$root.java: $!\n";
	    $validSfile = 0;
	}
	if ($eventCode{"AUE_$root"}) {
	    if ($validSfile) {
		print Sfile <<EOF;
/*
 * $notice
 */

package com.sun.audit;

// audit event:  $eventId = $eventCode{"AUE_$root"}

public class AuditEvent_$root extends AuditEvent {

EOF
	    }
	} else {
	    print STDERR "no event code for $eventId.  Is audit_event current?\n";
	}
	my $nativeParameterList = '';
	my $jniParameterList = '';
	my $specParameterList = '';
	my $jniStorageList = '';
	my $needCleanupTarget = 0;
	my $jniFreeList = '';

	my $haveStringDef = 0;
	my $haveCDef = 0;
	my $haveLengthDef = 0;
	my $haveStringArrayDef = 0;
	my $cntTermidDef = 0;
	my $jniDefine;
	my $needLocaleDefined = 0;
	my $jniADTalloc;
	if (defined $header && ($header > 0) ) {
	    $jniDefine = "union union_of_events	*event;\n" .
		"\tadt_session_data_t	*session;\n";
	    $jniADTalloc = '(union union_of_events *)adt_alloc_event';
	} else {
	    $jniDefine = "adt_event_data_t	*event;\n" .
		"\tadt_session_data_t	*session;\n";
	    $jniADTalloc = 'adt_alloc_event';
	}
	my $ref2;
	foreach $ref2 (@entries) {
	    my ($id, $type) = @$ref2;
	    my $jniRoot = $root . $id;
	    $jniRoot =~ s/_/_1/g;  # escape unicode "_"

	    my $p_event;
	    if (defined $header && ($header > 0) ) {
		$p_event = "event->d$header.adt_$root.$id";
	    } else {
		$p_event = "event->adt_$root.$id";
	    }

	    if ($type eq 'ADT_UINT32STAR') { # int array
	        $needLocaleDefined = 1;


	        $jniStorageList .= <<EOF;
	/* $id */
	length = (*env)->GetArrayLength(env, $id);
	$p_event =
	    (int *)malloc(length * sizeof (int));
	if ($p_event == NULL) {
		locale = I18N_SETUP;
		local_throw(env, except_class,
		    $noMemory);
		(void) setlocale(LC_MESSAGES, locale);
		goto cleanup;
	}
	(*env)->GetIntArrayRegion(env, $id, 0, length,
	    (int *)$p_event);
EOF


		$jniFreeList .= "\n\tif ($p_event != NULL)\n" .
		    "\t\tfree($p_event);\n";
		unless ($haveLengthDef) {
		    $haveLengthDef = 1;
		    $jniDefine .= "\tint\t\t\tlength;\n";
		}
		$nativeParameterList .= ",\n\t    int[]\t$id";
		$jniParameterList .= ",\n    jintArray\t$id";
		$specParameterList .= ", jintArray";
		$needCleanupTarget = 1;
	    } elsif (($type eq 'ADT_UIDSTAR') ||
		     ($type eq 'ADT_GIDSTAR')) { # gid_t array
		my $cType = 'uid_t';
		$cType = 'gid_t' if ($type eq 'ADT_GIDSTAR');
		$needLocaleDefined = 1;


		$jniStorageList .= <<EOF;
	/* $id */
	length = (*env)->GetArrayLength(env, $id);
	$p_event =
	    ($cType *)malloc(length * sizeof ($cType));
	if ($p_event == NULL) {
		locale = I18N_SETUP;
		local_throw(env, except_class,
		    $noMemory);
		(void) setlocale(LC_MESSAGES, locale);
		goto cleanup;
	}
	(*env)->GetIntArrayRegion(env, $id, 0, length,
	    (int *)$p_event);
EOF


		$jniFreeList .=
		    "\n\tif ($p_event != NULL)\n" .
		    "\t\tfree($p_event);\n";
		unless ($haveLengthDef) {
		    $haveLengthDef = 1;
		    $jniDefine .= "\tint\t\t\tlength;\n";
		}
		$nativeParameterList .= ",\n\t    int[]\t$id";
		$jniParameterList .= ",\n    jintArray\t$id";
		$specParameterList .= ", jintArray";
		$needCleanupTarget = 1;
	    } elsif ($type eq 'ADT_UINT64STAR') { # long array
	        $needLocaleDefined = 1;
	        $jniStorageList .= <<EOF;
	/* $id */
	length = (*env)->GetArrayLength(env, $id);
	$p_event =
	    (long *)malloc(length * sizeof (long long));
	if ($p_event == NULL) {
		locale = I18N_SETUP;
		local_throw(env, except_class,
		    $noMemory);
		(void) setlocale(LC_MESSAGES, locale);
		goto cleanup;
	}
	(*env)->GetLongArrayRegion(env, $id, 0, length,
	    $p_event);
EOF
		$jniFreeList .= "\n\tif ($p_event != NULL)\n" .
		    "\t\tfree($p_event);\n";
		unless ($haveLengthDef) {
		    $haveLengthDef = 1;
		    $jniDefine .= "\tint\t\t\tlength;\n";
		}
		$nativeParameterList .= ",\n\t    long[]\t$id";
		$jniParameterList .= ",\n    jlongArray\t$id";
		$specParameterList .= ", jlongArray";
		$needCleanupTarget = 1;
	    } elsif ($type eq 'ADT_CHAR') { # string in Java, char in C
		$jniStorageList .= <<EOF;

	/* $id */
	c = (char *)(*env)->GetStringUTFChars(env, $id, NULL);
	if (c == NULL)
		goto cleanup; /* exception thrown */
	$p_event = *c;
	(*env)->ReleaseStringUTFChars(env, $id, c);
EOF
		# no need to free anything
		unless ($haveCDef) {
		    $haveCDef = 1;
		    $jniDefine .= "\tchar\t\t\t*c\n";
		}
		$nativeParameterList .= ",\n\t    String\t$id";
		$jniParameterList .= ",\n    jstring\t$id";
		$specParameterList .= ", jstring";
	    } elsif ($type eq 'ADT_CHARSTAR') {
	        $needLocaleDefined = 1;
		$jniStorageList .= <<EOF;
	/* $id */
	if ($id != NULL) {
		string = (char *)(*env)->GetStringUTFChars(
		    env, $id, NULL);
		if (string == NULL)
			goto cleanup; /* exception thrown */
		$p_event = strdup(string);
		(*env)->ReleaseStringUTFChars(env, $id, string);
		if ($p_event == NULL) {
			locale = I18N_SETUP;
			local_throw(env, except_class,
			    $noMemory);
			(void) setlocale(LC_MESSAGES, locale);
			goto cleanup;
		}
	}
EOF
		$jniFreeList .= "\n\tif ($p_event != NULL)\n" .
		    "\t\tfree($p_event);\n";
		unless ($haveStringDef) {
		    $haveStringDef = 1;
		    $jniDefine .= "\tchar\t\t\t*string;\n";
		}
		$nativeParameterList .= ",\n\t    String\t$id";
		$jniParameterList .= ",\n    jstring\t$id";
		$specParameterList .= ", jstring";
		$needCleanupTarget = 1;
	    } elsif ($type eq 'ADT_CHAR2STAR') { # array of string
	        $needLocaleDefined = 1;
		$jniStorageList .= <<EOF;
	/* $id */
	length = (*env)->GetArrayLength(env, $id);
	$p_event = (char **)malloc(length
	    * sizeof (char *));
	if ($p_event == NULL) {
		locale = I18N_SETUP;
		local_throw(env, except_class,
		    $noMemory);
		(void) setlocale(LC_MESSAGES, locale);
		goto cleanup;
	}
	p = $p_event;
	for (i = 0; i < length; i++) {
		jString = (*env)->GetObjectArrayElement(env, $id, i);
		string = (char *)(*env)->GetStringUTFChars(
		    env, jString, NULL);
		if (string == NULL)
			goto cleanup; /* exception thrown */
		*p = strdup(string);
		(*env)->ReleaseStringUTFChars(env, jString, string);
		if (*p == NULL) {
			locale = I18N_SETUP;
			local_throw(env, except_class,
			    $noMemory);
			(void) setlocale(LC_MESSAGES, locale);
			while (p >= $p_event)
				free(*p--);
			goto cleanup;
		}
		p++;
	}
EOF
		$jniFreeList .=
		    "\n\tif ($p_event != NULL)\n" .
		    "\t\tfree($p_event);\n";
		unless ($haveStringArrayDef) {
		    unless ($haveStringDef) {
			$haveStringDef = 1;
			$jniDefine .= <<EOF;
	char			*string;
EOF
		    }
		    unless ($haveLengthDef) {
			$haveLengthDef = 1;
			$jniDefine .= <<EOF;
	int			length;
EOF
		    }
		    $haveStringArrayDef = 1;
		    $jniDefine .= <<EOF;
	int			i;
	char			**p;
	jstring			jString;
EOF
		}
		$nativeParameterList .= ",\n\t    String[]\t$id";
		$jniParameterList .= ",\n    jstring\t$id";
		$specParameterList .= ", jstring";
		$needCleanupTarget = 1;
	      } elsif ($type eq 'ADT_TERMIDSTAR') {
	        $needLocaleDefined = 1;

	        $jniStorageList .= <<EOF;
	/* $id */
	hostname$cntTermidDef = (char *)(*env)->GetStringUTFChars(env, $id, NULL);

	if (adt_load_hostname((const char *)hostname$cntTermidDef, &termid$cntTermidDef)) {
		local_throw(env, except_class,
			gettext("hostname lookup failed"));
	}
	$p_event = termid$cntTermidDef;

	(*env)->ReleaseStringUTFChars(env, $id, hostname$cntTermidDef);
EOF

		$jniFreeList .= "\n\tif (hostname$cntTermidDef != NULL)\n" .
		    "\t\tfree(hostname$cntTermidDef);\n";
		$jniFreeList .= "\n\tif (termid$cntTermidDef != NULL)\n" .
		    "\t\tfree(termid$cntTermidDef);\n";

		$jniDefine .= "\tchar\t\t\t*hostname$cntTermidDef;\n";
		$jniDefine .= "\tadt_termid_t\t\t*termid$cntTermidDef;\n"; #djdj

		$cntTermidDef++;

		my ($nativeParameter, $jniParameter) = @{$java_jni{$type}};
		$nativeParameterList .= ",\n\t    $nativeParameter\t$id";
		$jniParameterList .= ",\n    $jniParameter\t$id";
		$specParameterList .= ", $jniParameter";
		$needCleanupTarget = 1;
	    } else {  # all others are primitive types
		$jniStorageList .= "\n\t$p_event = $id;\n";
		my ($nativeParameter, $jniParameter) = @{$java_jni{$type}};
		$nativeParameter = "$nativeParameter\t"
		    if length $nativeParameter < 4;  # why?
		$nativeParameterList .= ",\n\t    $nativeParameter\t$id";
		$jniParameterList .= ",\n    $jniParameter\t$id";
		$specParameterList .= ", $jniParameter";
	    }
	}
	if ($needLocaleDefined) {
		$jniDefine .= <<EOF
	char			*locale;
EOF
	}
	my $genericOverride = '';
	my $idParameter = $eventId;
	$idParameter =~ s/AUE_/ADT_/;
	if ($eventType eq 'generic') {
	    $genericOverride = ', jint eventId';
	    $idParameter = 'eventId';
	}
	$jniFreeList = "\tcleanup:\n" . $jniFreeList if $needCleanupTarget;

	print Cfile qq{/* ARGSUSED */
JNIEXPORT void JNICALL
$jniPutEvent(
    JNIEnv	*env,
    jobject	self,
    jbyteArray	jsession$genericOverride,
    jint	status,
    jint	ret_val$jniParameterList)
{
	$jniDefine
	(void) j2c_pointer(env, jsession, (char **)&session);

	event = $jniADTalloc(session, $idParameter);

$jniStorageList
	(void) adt_put_event((adt_event_data_t *)event, status, ret_val);

$jniFreeList
	adt_free_event((adt_event_data_t *)event);
}
};
	print MapFile qq{
	$jniPutEvent; };
	my $overrideParameter = '';
	if ($eventType eq 'generic') {
	    $overrideParameter = 'int eventId,';
	    my @allowed = @$allowedIds;
	    if (@allowed) {
		my $i;
		if ($validSfile) {
		    print Sfile "\t// Allowed values for eventId in putEvent:\n";
		    for ($i = 0; $i <= $#allowed; $i++) {
			my $idNo = $externalIdNo{$allowed[$i]};
			$allowed[$i] =~ s/AUE_/ADT_/;
			print Sfile "\tstatic final int $allowed[$i] = ",
			     "$idNo;\n";
		    }
		    print Sfile "\n";
		}
	    } else {
		print STDERR "Generic event with no allowed instances: $eventId\n";
	    }
	}
	if ($validSfile) {
	    print Sfile <<EOF;
	private native void $javaPutEvent(byte[]session, $overrideParameter
	    int status, int ret_val$nativeParameterList);

	public AuditEvent_$root(AuditSession session)
		throws Exception
	{
		super(session);
	}

EOF
	    my $javaParameterList = '';
	    foreach $ref2 (@entries) {
		my ($id, $type, $format, $jComment, $required) = @$ref2;

		# generate java native method prototypes
		# and the corresponding C method implementation
		
		my $javaMethodName = "$id";
		my $javaStorageName = $javaMethodName . '_val';
		my $jniMethodName = $root . $id;
		my $storage;
		my $enumUsage = '';
		my $jParam = @{$java_jni{$type}}[0];
		my $comment = '';
		if ($required) {
		    if ($format ne 'NULL') {
			$comment = "\t// (required) formatted:  $format";
		    } else {
			$comment = "\t// required";
		    }
		} else {
		    if ($format ne 'NULL') {
			$comment = "\t// (optional) formatted:  $format";
		    } else {
			$comment = "\t// optional";
		    }
		}
		if (($type eq 'ADT_UINT32STAR') ||
		    ($type eq 'ADT_UIDSTAR') ||
		    ($type eq 'ADT_GIDSTAR')) { # int array
		    $storage = "int[] $javaStorageName" . ($required ?
							   ' = {}' : '');
		    $javaParameterList .= ",\n\t\t\t    $javaStorageName";
		} elsif ($type eq 'ADT_UINT64STAR') { # long array
		    $storage = "long[] $javaStorageName" . ($required ?
							    ' = {}' : '');
		    $javaParameterList .= ",\n\t\t\t    $javaStorageName";
		} elsif (($type eq 'ADT_CHARSTAR') ||
			 ($type eq 'ADT_CHAR')) { # string
		    $storage = "String $javaStorageName" . ($required ?
							    ' = ""' : '');
		    $javaParameterList .= ",\n\t\t\t    $javaStorageName";
		} elsif ($type eq 'ADT_CHAR2STAR') { # array of string
		    $storage = "String[] $javaStorageName" . ($required ?
							      ' = {}' : '');
		    $javaParameterList .= ",\n\t\t\t    $javaStorageName";
		} elsif ($type eq 'ADT_TERMIDSTAR') { # array of string
		    $storage = "String $javaStorageName" . ($required ?
							    ' = ""' : '');
		    $javaParameterList .= ",\n\t\t\t    $javaStorageName";
		} else {  # all others are primitive types
		    $storage = "$jParam $javaStorageName = 0";
		    $javaParameterList .= ",\n\t\t\t    $javaStorageName";
		    $enumUsage = "\n\t// See $jComment in AuditEvent.java for valid values"
			if $jComment;
		}
		print Sfile <<EOF;
$enumUsage
	private $storage;$comment
	public void $javaMethodName($jParam setTo)
	{
		$javaStorageName = setTo;
	}
EOF
	    }	# end foreach (@entries)
	    if ($eventType eq 'generic') {
		print Sfile <<EOF;

	public void putEvent(int status, int ret_val, int eventId)
	{
		byte[]	session = super.sh.getSession();

		if ((super.sh.AuditIsOn) && (super.sh.ValidSession))
			$javaPutEvent(session, eventId,
			    status, ret_val$javaParameterList);
	}
}
EOF
	    } else { 
		print Sfile <<EOF;

	public void putEvent(int status, int ret_val)
	{
		byte[]	session = super.sh.getSession();

		if ((super.sh.AuditIsOn) && (super.sh.ValidSession))
			$javaPutEvent(session, status, ret_val$javaParameterList);
	}
}
EOF
	    }
	    close Sfile;
	}	# end if ($validSfile);
    }
 
    # write trailers
    print Jfile <<EOF;

}
EOF
    print MapFile <<EOF;

    local:
	*;
};
EOF
    close Cfile;
    close Jfile;
    close MapFile;
}

sub generateTableC {
    my $event = shift;
    my $eventId = shift;
    my $eventType = shift;
    my $eventHeader = shift;
    my $omit = shift;

    my %tokenType = (
		  'acl'			=> 'AUT_ACL',
		  'arbitrary'		=> 'AUT_ARBITRARY',
		  'arg'			=> 'AUT_ARG',
		  'attr'		=> 'AUT_ATTR',
		  'command'		=> 'AUT_CMD',
		  'command_1'		=> 'ADT_CMD_ALT',	# dummy token id
		  'date'		=> 'AUT_TEXT',
		  'exec_args'   	=> 'AUT_EXEC_ARGS',
		  'exec_env'    	=> 'AUT_EXEC_ENV',
		  'exit'        	=> 'AUT_EXIT',
		  'file'        	=> 'AUT_FILE',
		  'fmri'        	=> 'AUT_FMRI',
		  'groups'      	=> 'AUT_GROUPS',
	#	  'header'      	=> 'AUT_HEADER',	# not used
		  'in_addr'     	=> 'AUT_IN_ADDR',
		  'ipc'         	=> 'AUT_IPC',
		  'ipc_perm'    	=> 'AUT_IPC_PERM',
		  'iport'		=> 'AUT_IPORT',
		  'label'		=> 'AUT_LABEL',
		  'newgroups'   	=> 'AUT_NEWGROUPS',
		  'opaque'      	=> 'AUT_OPAQUE',
		  'path'        	=> 'AUT_PATH',
		  'path_list'		=> '-AUT_PATH',		# dummy token id
		  'process'     	=> 'AUT_PROCESS',
		  'priv_effective'	=> 'ADT_AUT_PRIV_E',	# dummy token id
		  'priv_limit'		=> 'ADT_AUT_PRIV_L', 	# dummy token id
		  'priv_inherit'	=> 'ADT_AUT_PRIV_I',	# dummy token id
		  'return'      	=> 'AUT_RETURN',
		  'seq'         	=> 'AUT_SEQ',
		  'socket'      	=> 'AUT_SOCKET',
		  'socket-inet' 	=> 'AUT_SOCKET_INET',
		  'subject'     	=> 'AUT_SUBJECT',
		  'text'        	=> 'AUT_TEXT',
		  'tid'          	=> 'AUT_TID',
	#	  'trailer'     	=> 'AUT_TRAILER',	# not used
		  'uauth'		=> 'AUT_UAUTH',
		  'user'		=> 'AUT_USER',
		  'zonename'		=> 'AUT_ZONENAME'
		 );

    my @xlateEntryList = ();
    my @jniEntryList = ();

    my $external = $event->getExternal();
    my $internal = $event->getInternal();

    unless ($external) {
	print STDERR "No external object captured for event $eventId\n";
	return;
    }
    unless ($internal) {
	print STDERR "No internal object captured for event $eventId\n";
	return;
    }
    my @entryRef = $internal->getEntries();
    my $entryRef;
    my @tokenOrder = ();
    my $firstTokenIndex = 0; # djdj not used yet, djdj BUG!
    			     # needs to be used by translate table

    if ($internal->isReorder()) { # prescan the entry list to get the token order
      my @inputOrder;
      foreach $entryRef (@entryRef) {
	my ($intEntry, $entry) = @$entryRef;
	push (@inputOrder, $intEntry->getAttr('order'));
      }

      my $i; # walk down the inputOrder list once
      my $k = 1; # discover next in line
      my $l = 0; # who should point to next in line
      for ($i = 0; $i <= $#inputOrder; $i++) {
	my $j;
	for ($j = 0; $j <= $#inputOrder; $j++) {
	  if ($k == $inputOrder[$j]) {
	    if ($k == 1) {
	        $firstTokenIndex = $j;
	    } else {
	        $tokenOrder[$l] = "&(selfReference[$j])";
	    }
	    $l = $j;
	    last;
	  }
	}
	$k++;
      }
      $tokenOrder[$l] = 'NULL';
    }
    else { # default order -- input order same as output
      my $i;
      my $j;
      for ($i = 0; $i < $#entryRef; $i++) {
	my $j = $i + 1;
	$tokenOrder[$i] = "&(selfReference[$j])";
      }
      $tokenOrder[$#entryRef] = 'NULL';
    }

    my $sequence = 0;
    foreach $entryRef (@entryRef) {
      my ($intEntry, $entry) = @$entryRef;
      my $entryId = $entry->getAttr('id');

      my ($extEntry, $unusedEntry, $tokenId) =
	$external->getEntry($entryId);
      my $opt = $extEntry->getAttr('opt');

      if ($opt eq 'none') {
	if (defined ($doc->getToken($tokenId))) {
	  if (defined ($tokenType{$tokenId})) {
	    $tokenId = $tokenType{$tokenId};
	  }
	  else {
	    print STDERR "token id $tokenId not implemented\n";
	  }
	}
	else {
	  print STDERR "token = $tokenId is undefined\n";
	  $tokenId = 'error';
	}
	my ($xlate, $jni) =
	  formatTableEntry ('', $tokenId, $eventId, '', 0, 0, $tokenOrder[$sequence],
			    'NULL', '');
	push (@xlateEntryList, $xlate);
	push (@jniEntryList, @$jni);
      }
      else {
	my $dataType = $extEntry->getAttr('type');
	$dataType =~ s/\s+//g;   # remove blanks (char * => char*)

	my $enumGroup = '';
	if ($dataType =~ /^msg/i) {
	    $enumGroup = $dataType;
	    $enumGroup =~ s/^msg\s*//i;
	    $enumGroup = 'adt_' . $enumGroup;
	}
	my $required = ($opt eq 'required') ? 1 : 0;
	my $tsol = 0;
	my $tokenId = $intEntry->getAttr('token');
	my $token;
	my $tokenName;
	my $tokenFormat = $intEntry->getAttr('format');
	if (defined ($tokenFormat)) {
	  $tokenFormat = "\"$tokenFormat\"";
	}
	else {
	  $tokenFormat = 'NULL';
	}
	
	if (defined ($token = $doc->getToken($tokenId))) {
	  $tsol = (lc $token->getUsage() eq 'tsol') ? 1 : 0;
	  if (defined ($tokenType{$tokenId})) {
	    $tokenName = $tokenType{$tokenId};
	  }
	  else {
	    print STDERR "token id $tokenId not implemented\n";
	  }
	}
	else {
	  print STDERR 
	    "$tokenId is an unimplemented token ($entryId in $eventId)\n";
	  $tokenName = 'AUT_TEXT';
	}
	my ($xlate, $jni) =
	  formatTableEntry($entryId, $tokenName, $eventId, $dataType, $required,
			   $tsol, $tokenOrder[$sequence], $tokenFormat,
			   $enumGroup, (uc $omit eq 'JNI'));
	push (@xlateEntryList, $xlate);
	push (@jniEntryList, @$jni);
      }
      $sequence++;
    }
    $jniEventTable{$eventId} = [\@jniEntryList, $eventType,
				$external->getAllowedTypes(), $eventHeader]
	unless (uc $omit eq 'JNI') || ($omit eq 'always');
}

sub formatTableEntry {
    my ($id, $token, $eventId, $type, $required, $tsol, $sequence, $format, $enumGroup,
	$omitJNI) = @_;


    # does this map belong in the xml source?  (at least the defaults?)
    # fill in the default value only if it is other than zero.
    #		      base type		    adt name,	default value
    my %entryDef = ( 'au_asid_t'       	=> ['ADT_UINT32',	''],
		     'uint_t'		=> ['ADT_UINT32',      	''],
		     'int'		=> ['ADT_INT',		''],
		     'int32_t'		=> ['ADT_INT32',	''],
		     'uid_t'		=> ['ADT_UID',		'AU_NOAUDITID'],
		     'gid_t'		=> ['ADT_GID',		'AU_NOAUDITID'],
		     'uid_t*'		=> ['ADT_UIDSTAR',	''],
		     'gid_t*'		=> ['ADT_GIDSTAR',	''],
		     'char'		=> ['ADT_CHAR',		''],
		     'char*'		=> ['ADT_CHARSTAR',	''],
		     'char**'		=> ['ADT_CHAR2STAR',	''],
		     'long'		=> ['ADT_LONG',		''],
		     'pid_t'		=> ['ADT_PID',		''],
		     'priv_set_t*'	=> ['ADT_PRIVSTAR',	''],
		     'ulong_t'		=> ['ADT_ULONG',	''],
		     'uint16_t',	=> ['ADT_UINT16',	''],
		     'uint32_t'		=> ['ADT_UINT32',	''],
		     'uint32_t*'	=> ['ADT_UINT32STAR',	''],
		     'uint32_t[]'	=> ['ADT_UINT32ARRAY',  ''],
		     'uint64_t'		=> ['ADT_UINT64',	''],
		     'uint64_t*'	=> ['ADT_UINT64STAR',	''],
		     'm_label_t*'	=> ['ADT_MLABELSTAR',	''],
		    );
    my $xlateLabel = $uniLabel.$xlateUniLabelInc;
    my $xlateLabelInc = 0;
    my $xlateLine = '';
    my @jniLine = ();

	# the list handling should be a simple loop with a loop of one
        # falling out naturally.

    unless ($type =~ /,/) {	# if list, then generate sequence of entries
      my $dataType;
      my $dataSize;
      my $xlateLabelRef = '';

      my $arraySize = '';
      $arraySize = $1 if ($type =~ s/\[(\d+)\]/[]/);

      my $entryType = ${$entryDef{$type}}[0];

      my @xlateType = ();	# for adt_xlate.c
      my $typeCount = 1;

      if ($entryType) {
	$dataType = $entryType;
	$type =~ s/([^*]+)\s*(\*+)/$1 $2/;
	$type =~ s/\[\]//;
	$dataSize = "sizeof ($type)";
	if ($arraySize) {
		$dataSize = "$arraySize * " . $dataSize;
	}
	$xlateLine = "{{$dataType, $dataSize}}";
	push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
      } elsif ($type eq '') {
	  $xlateLabelRef = 'NULL';
      } elsif ($type =~ /^msg/i) {
	$type =~ s/^msg//i;
	$dataType = 'ADT_MSG';
	my $dataEnum = 'ADT_LIST_' . uc $type;
	$xlateLine = "{{$dataType, $dataEnum}}";
	push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
      } elsif ($type =~ /time_t/i) {
	$dataType = 'ADT_DATE';
	$dataSize = "sizeof (time_t)";
	$xlateLine = "{{$dataType, $dataSize}}";
	push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
      } elsif ($type =~ /termid/i) {
	$dataType = 'ADT_TERMIDSTAR';
	$dataSize = "sizeof (au_tid_addr_t *)";
	$xlateLine = "{{$dataType, $dataSize}}";
	push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
      } elsif ($omitJNI) {
	$xlateLabelRef = 'NULL';
      } else {
	print STDERR "$type is not an implemented data type\n";
	$xlateLabelRef = 'NULL';
      }
      $xlateLabelRef = '&' . $xlateLabel . '[0]'
	unless $xlateLabelRef eq 'NULL';

      # "EOL" is where a comma should go unless end of list
      $xlateLine = "{$token,\t1,\t$xlateLabelRef,\t$sequence,\n" .
	  "\t\t0,\t$required,\t$tsol,\t$format}EOL";
      
    } else {	# is a list
      my @type = split(/,/, $type);
      my @arraySize = ();
      my @id   = split(/,/, $id);
      my @jniId  = @id;
      my $dataType;
      my $typeCount = ($#type + 1);
      my @xlateType = ();
      my @default = ();

      foreach my $dtype (@type) {
	my $jniId = shift @jniId;
	my $id = shift @id;
	my $arraySize = '';
	$arraySize = $1 if ($dtype =~ s/\[(\d+)\]/[]/);

	my $entryType = ${$entryDef{$dtype}}[0];
	if ($entryType) {
	  my $type = $dtype;
	  $type =~ s/([^*]+)\s*(\*+)/$1 $2/;
	  $type =~ s/\[\]//;

	  my $sizeString = "sizeof";
	  $sizeString = "$arraySize * " . $sizeString if $arraySize;
	  push (@xlateType, "\{$entryType, $sizeString ($type)\}");
	  push (@jniLine, [$jniId, $entryType, $format, $enumGroup, $required]);
	} elsif ($type =~ /^msg/i) {
	  $type =~ s/^msg//i;
	  $dataType = 'ADT_MSG';
	  my $dataEnum = 'ADT_LIST_' . uc $type;
	  push (@xlateType, "\{$dataType, $dataEnum\}};");
	  push (@jniLine, [$jniId, $dataType, $format, $enumGroup, $required]);
	} elsif ($type =~ /time_t/i) {
	  $dataType = 'ADT_DATE';
	  push (@xlateType, "\{$entryType, sizeof ($type)\}");
	  push (@jniLine, [$jniId, $entryType, $format, $enumGroup, $required]);
	} elsif ($type =~ /termid/i) {
	  $dataType = 'ADT_TERMIDSTAR';
	  push (@xlateType, "\{$dataType, sizeof (au_tid_addr_t *)\}");
	  push (@jniLine, [$jniId, $dataType, $format, $enumGroup, $required]);
	} elsif ($omitJNI) {
	  # nothing to do.
	} else {
	  print STDERR "$dtype is not an implemented data type\n";
	}
	if (${$entryDef{$dtype}}[1]) {
	  push (@default, $id, ${$entryDef{$dtype}}[1]);
	}
      }
      my $xlateArray = "\[$typeCount\] =\t{" . join(",\n\t\t\t\t", @xlateType) . "};";
      
      $xlateLine =
	"{$token,\t$typeCount,\t&$xlateLabel\[0\],\t$sequence,\n" .
        "\t\t0,\t$required,\t$tsol,\t$format}EOL";
    }
    $xlateUniLabelInc++ if $xlateLabelInc;
    return ($xlateLine, \@jniLine);
}

sub generateMsgLists {
    my $textList = shift;

    my $textName = $textList->getId();
    my $header = $textList->getHeader();
    my $start = $textList->getMsgStart();
    my $public = $textList->getMsgPublic();
    my $deprecated = $textList->getDeprecated();

    print "$textName starts at $start\n" if $debug;

    my $entry;
    my @entry;
    while ($entry = $textList->getNextMsg()) {
        if ($debug) {
	    my ($id, $text) = split(/\s*::\s*/, $entry);
	    print "   $id = $text\n";
	}
	unshift (@entry, $entry);
    }
    $msg_list{$textName} =
	[\@entry, [$header, $start, $public, $deprecated]];
}
sub readAuditEventFile {
    my $eventListFile = shift;

  open(Event, $eventListFile)
    or die "can't open $eventListFile: $!\n";
  while(<Event>) {
    next if /^\s*#/;
    next if /^\s*$/;
    my ($value, $name) = split(/\s*:\s*/);
    next if $value < 6000;
    $eventCode{$name} = $value;
  }
  close Event;
}
